package common;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jodd.util.ClassLoaderUtil;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;

public class ConfigHelper
{
    private static final Properties configProperties = new Properties();
    private static final String PRO_FILE = "config.properties";
    private static final String DEFUALT_LOCAL_PRO_FILE = "local.properties";
    private static final String DEFUALT_PUBLISH_PRO_FILE = "publish-config.properties";
    private static boolean ISDEBUG = true;

    public static final String SPLIT_FLAG_DOT = ".";
    public static final String SPLIT_FLAG_COMMA = ",";
    public static final String SPLIT_FLAG_ASTERISK = "*";
    public static final String SPLIT_FLAG_POUND = "#";
    public static final String SPLIT_FLAG_AT = "@";
    public static final String SPLIT_FLAG_START = SPLIT_FLAG_AT + "[";
    public static final String SPLIT_FLAG_END = "]";

    public static final String REGEX = "([\\w.]+)+";

    static
    {
        try
        {
            URL url = ClassLoaderUtil.getResourceUrl(PRO_FILE);
            configProperties.load(url.openStream());

            if (configProperties.containsKey("service.isDebug"))
            {
                ISDEBUG = BooleanUtils.toBoolean(getStringProperty("service.isDebug", "true"));
            }

            String filePaths = null;
            if (ISDEBUG)
            {
                filePaths = getStringProperty("service.import.config", DEFUALT_LOCAL_PRO_FILE);
            }
            else
            {
                filePaths = getStringProperty("service.import.publicConfig", DEFUALT_PUBLISH_PRO_FILE);
            }
            String[] propertiesFilePaths = null;
            if (StringUtils.contains(filePaths, SPLIT_FLAG_COMMA))
            {
                propertiesFilePaths = StringUtils.split(filePaths, SPLIT_FLAG_COMMA);
            }
            else if (filePaths.length() > 0)
            {
                propertiesFilePaths = new String[] { filePaths };
            }

            for (String path : propertiesFilePaths)
            {
                System.out.println("Path :" + path);
                // should be according the path's order to override the properties key/value.
                url = ClassLoaderUtil.getResourceUrl(path);
                configProperties.load(url.openStream());
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    public static String getScopeStringProperty(String scope, String key)
    {
        if (StringUtils.endsWith(scope, SPLIT_FLAG_DOT))
        {
            return getStringProperty(scope + key);
        }
        return getStringProperty(scope + SPLIT_FLAG_DOT + key);
    }

    public static String getScopeStringProperty(String scope, String key, String defaultValue)
    {
        String value = getScopeStringProperty(scope, key);
        return StringUtils.isBlank(value) ? defaultValue : value;
    }

    public static String getStringProperty(String key)
    {

        String value = "";
        if (configProperties.containsKey(key))
        {
            value = configProperties.getProperty(key);
        }
        else
        {
            System.err.println("Can not get property [" + key + "] in config.properties file.");
        }
        return value;
    }

    public static String getStringProperty(String key, String defaultValue)
    {
        String value = null;
        if (configProperties.containsKey(key))
        {
            value = configProperties.getProperty(key);
        }
        return StringUtils.isBlank(value) ? defaultValue : value;

    }

    public static int getNumberProperty(String key)
    {
        int value = 0;
        String sValue = getStringProperty(key);
        if (NumberUtils.isNumber(sValue))
        {
            value = Integer.parseInt(sValue);
        }
        return value;
    }

    public static int getNumberProperty(String key, int defaultValue)
    {
        int value = 0;
        String sValue = getStringProperty(key);
        if (StringUtils.isBlank(sValue))
        {
            return defaultValue;
        }
        if (NumberUtils.isNumber(sValue))
        {
            value = Integer.parseInt(sValue);
        }
        return value;
    }

    public static int getScopeNumberProperty(String scope, String key, int defaultValue)
    {
        String sValue = getScopeStringProperty(scope, key);
        if (StringUtils.isBlank(sValue))
        {
            return defaultValue;
        }
        if (NumberUtils.isNumber(sValue))
        {
            return NumberUtils.toInt(sValue);
        }
        return defaultValue;
    }

    /**
     * 1 get property with root.children.name,if the key isn't exist. then get the property with
     * roo.name,if also isn't exist. return the default value.<br>
     * 2 if children have split flag. such a dot symbol,then we need split with this symbol and loop
     * up every part of the children .
     * 
     * @param root
     * @param children
     * @param name
     * @param defaultValue
     * @return
     */
    public static String getUpwardProperty(String parent, String children, String name, String defaultValue)
    {
        String value = getUpwardProperty(parent, children, name);
        if (StringUtils.isNotBlank(value))
        {
            return value;
        }
        return defaultValue;
    }

    public static String getUpwardProperty(String parent, String children, Object obj)
    {
        if (null == obj)
        {
            return null;
        }
        String name = obj.toString();
        // get the full name key
        String value = getScopeStringProperty(parent, String.format("%s" + SPLIT_FLAG_DOT + "%s", children, name));
        if (StringUtils.isNotBlank(value))
        {
            return value;
        }
        // if children have split flag
        if (StringUtils.contains(children, SPLIT_FLAG_DOT))
        {
            String[] split = StringUtils.split(children, SPLIT_FLAG_DOT);
            for (int i = split.length - 2; i > 0; i--)
            {
                String subCh = StringUtils.join(split, SPLIT_FLAG_DOT, 0, i);
                value = getScopeStringProperty(parent, String.format("%s" + SPLIT_FLAG_DOT + "%s", subCh, name));
                if (StringUtils.isNotBlank(value))
                {
                    return value;
                }
            }
        }
        // get the parent with the parameter name.
        value = getScopeStringProperty(parent, name);
        if (StringUtils.isNotBlank(value))
        {
            return value;
        }
        return null;
    }

    /**
     * currently just support a word fuzzy search.
     * 
     * @param key
     * @return
     */
    public static String[] getPossibleKeys(String key)
    {
        if (!StringUtils.contains(key, SPLIT_FLAG_ASTERISK))
        {
            return new String[] { key };
        }
        String newKey = StringUtils.replace(key, SPLIT_FLAG_DOT, SPLIT_FLAG_POUND);
        ArrayList<String> selects = new ArrayList<String>();

        String[] newArray = StringUtils.split(newKey, SPLIT_FLAG_ASTERISK);

        if (newArray.length > 2)
        {
            // currently just support a word fuzzy search.
            return null;
        }

        String beforeStr = newArray[0];
        String afterStr = newArray.length == 2 ? newArray[1] : "";
        String[] merge = new String[3];
        merge[0] = StringUtils.substringAfterLast(beforeStr, SPLIT_FLAG_POUND);
        merge[1] = REGEX;
        merge[2] = StringUtils.substringBeforeLast(afterStr, SPLIT_FLAG_POUND);

        beforeStr = StringUtils.replace(beforeStr, merge[0], "");
        afterStr = StringUtils.replace(afterStr, merge[2], "");

        String rex = StringUtils.join(new String[] { beforeStr, StringUtils.join(merge), afterStr });
        for (Object propertyKey : configProperties.keySet())
        {
            String proName = propertyKey.toString();
            String repProName = StringUtils.replace(proName, SPLIT_FLAG_DOT, SPLIT_FLAG_POUND);
            if (StringUtils.isNotBlank(repProName) && repProName.matches(rex))
            {
                selects.add(proName);
                /**
                 * Matcher mat = pattern.matcher(repProName); if (mat.find()) { }
                 */
            }
        }
        return selects.toArray(new String[selects.size()]);
    }

    /**
     * default very parameter don't end with 'dot' symol.
     * 
     * @param parent
     * @param children
     * @param name
     * @return
     */
    public static String[] getPossibleUpwardKeys(String parent, String children, String name)
    {
        parent = StringUtils.endsWith(parent, SPLIT_FLAG_DOT) ? parent : parent + SPLIT_FLAG_DOT;
        String newChildren = StringUtils.endsWith(children, SPLIT_FLAG_DOT) ? children : children + SPLIT_FLAG_DOT;
        String key = StringUtils.join(new String[] { parent, newChildren, name });
        if (!StringUtils.contains(key, SPLIT_FLAG_ASTERISK))
        {
            return new String[] { key };
        }

        String[] keys = getPossibleKeys(key);
        if (keys.length > 0)
        {
            return keys;
        }
        if (StringUtils.contains(children, SPLIT_FLAG_DOT))
        {
            String[] split = StringUtils.split(children, SPLIT_FLAG_DOT);
            for (int i = split.length - 2; i > 0; i--)
            {
                String subCh = StringUtils.join(split, SPLIT_FLAG_DOT, 0, i);
                key =
                                StringUtils.join(new String[] {
                                                parent,
                                                StringUtils.endsWith(subCh, SPLIT_FLAG_DOT) ? subCh : subCh
                                                                + SPLIT_FLAG_DOT, name });
                keys = getPossibleKeys(key);
                if (keys.length > 0)
                {
                    return keys;
                }
            }
        }
        key = StringUtils.join(new String[] { parent, name });
        keys = getPossibleKeys(key);
        if (keys.length > 0)
        {
            return keys;
        }

        return null;
    }
}
